/**
 ****************************************************************************************************
 * @file        can.c
 * @author      正点原子团队(ALIENTEK)
 * @version     V1.0
 * @date        2020-04-24
 * @brief       CAN 驱动代码
 * @license     Copyright (c) 2020-2032, 广州市星翼电子科技有限公司
 ****************************************************************************************************
 * @attention
 *
 * 实验平台:正点原子 STM32F103开发板
 * 在线视频:www.yuanzige.com
 * 技术论坛:www.openedv.com
 * 公司网址:www.alientek.com
 * 购买地址:openedv.taobao.com
 *
 * 修改说明
 * V1.0 20200424
 * 第一次发布
 *
 ****************************************************************************************************
 */

#include "./BSP/CAN/can.h"
#include "./BSP/LED/led.h"
#include "./SYSTEM/delay/delay.h"
#include "./SYSTEM/usart/usart.h"

CAN_HandleTypeDef   g_canx_handler;     /* CANx句柄 */
CAN_TxHeaderTypeDef g_canx_txheader;    /* 发送参数句柄 */
CAN_RxHeaderTypeDef g_canx_rxheader;    /* 接收参数句柄 */

/* 实际速度相关全局变量 */
volatile int32_t g_actual_speed = 0;         /* 实际速度值 */
volatile uint8_t g_speed_data_updated = 0;   /* 速度数据更新标志 */

/**
 * @brief       CAN初始化
 * @param       tsjw    : 重新同步跳跃时间单元.范围: 1~3;
 * @param       tbs2    : 时间段2的时间单元.范围: 1~8;
 * @param       tbs1    : 时间段1的时间单元.范围: 1~16;
 * @param       brp     : 波特率分频器.范围: 1~1024;
 *   @note      以上4个参数, 在函数内部会减1, 所以, 任何一个参数都不能等于0
 *              CAN挂在APB1上面, 其输入时钟频率为 Fpclk1 = PCLK1 = 36Mhz
 *              tq     = brp * tpclk1;
 *             
 *
 * @param       mode    : CAN_MODE_NORMAL,  正常模式;
                          CAN_MODE_LOOPBACK,回环模式;
 * @retval      0,  初始化成功; 其他, 初始化失败;
 */
uint8_t can_init(void)
{
  g_canx_handler.Instance = CAN1;
  g_canx_handler.Init.Prescaler = 4;                /* 分频系数(Fdiv)为brp+1 */
  g_canx_handler.Init.Mode = CAN_MODE_NORMAL;                    /* 模式设置 */
  g_canx_handler.Init.SyncJumpWidth = CAN_SJW_1TQ;           /* 重新同步跳跃宽度(Tsjw)为tsjw+1个时间单位 CAN_SJW_1TQ~CAN_SJW_4TQ */
  g_canx_handler.Init.TimeSeg1 = CAN_BS1_15TQ;                /* tbs1范围CAN_BS1_1TQ~CAN_BS1_16TQ */
  g_canx_handler.Init.TimeSeg2 = CAN_BS2_2TQ;                /* tbs2范围CAN_BS2_1TQ~CAN_BS2_8TQ */
  g_canx_handler.Init.TimeTriggeredMode = DISABLE;    /* 非时间触发通信模式 */
  g_canx_handler.Init.AutoBusOff = DISABLE;           /* 软件自动离线管理 */
  g_canx_handler.Init.AutoWakeUp = DISABLE;           /* 睡眠模式通过软件唤醒(清除CAN->MCR的SLEEP位) */
  g_canx_handler.Init.AutoRetransmission = ENABLE;    /* 禁止报文自动传送 */
  g_canx_handler.Init.ReceiveFifoLocked = DISABLE;    /* 报文不锁定,新的覆盖旧的 */
  g_canx_handler.Init.TransmitFifoPriority = DISABLE; /* 优先级由报文标识符决定 */
    
  if (HAL_CAN_Init(&g_canx_handler) != HAL_OK)
  {
    printf("CAN Init Failed\r\n");
    return 1;
  }

#if CAN_RX0_INT_ENABLE
  /* 使用中断接收 */
  __HAL_CAN_ENABLE_IT(&g_canx_handler, CAN_IT_RX_FIFO0_MSG_PENDING); /* FIFO0消息挂号中断允许 */
  HAL_NVIC_EnableIRQ(USB_LP_CAN1_RX0_IRQn);                          /* 使能CAN中断 */
  HAL_NVIC_SetPriority(USB_LP_CAN1_RX0_IRQn, 1, 0);                  /* 抢占优先级1，子优先级0 */
#endif

  CAN_FilterTypeDef sFilterConfig;

  /*配置CAN过滤器*/
  sFilterConfig.FilterBank = 0;                         /* 过滤器0 */
  sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK;     /* 标识符屏蔽位模式 */
  sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT;    /* 长度32位位宽*/
  sFilterConfig.FilterIdHigh = 0x0000;                  /* 32位ID */
  sFilterConfig.FilterIdLow = 0x0000;
  sFilterConfig.FilterMaskIdHigh = 0x0000;              /* 32位MASK */
  sFilterConfig.FilterMaskIdLow = 0x0000;
  sFilterConfig.FilterFIFOAssignment = CAN_FILTER_FIFO0;    /* 过滤器0关联到FIFO0 */
  sFilterConfig.FilterActivation = CAN_FILTER_ENABLE;       /* 激活滤波器0 */
  sFilterConfig.SlaveStartFilterBank = 14;

  /* 过滤器配置 */
  if (HAL_CAN_ConfigFilter(&g_canx_handler, &sFilterConfig) != HAL_OK)
  {
    return 2;
  }

  /* 启动CAN外围设备 */
  if (HAL_CAN_Start(&g_canx_handler) != HAL_OK)
  {
    return 3;
  }

  return 0;
}

/**
 * @brief       CAN底层驱动，引脚配置，时钟配置，中断配置
                此函数会被HAL_CAN_Init()调用
 * @param       hcan:CAN句柄
 * @retval      无
 */
void HAL_CAN_MspInit(CAN_HandleTypeDef *hcan)
{
  if (CAN1 == hcan->Instance)
  {
    CAN_RX_GPIO_CLK_ENABLE();       /* CAN_RX脚时钟使能 */
    CAN_TX_GPIO_CLK_ENABLE();       /* CAN_TX脚时钟使能 */
    __HAL_RCC_CAN1_CLK_ENABLE();    /* 使能CAN1时钟 */

    GPIO_InitTypeDef gpio_initure;

    gpio_initure.Pin = CAN_TX_GPIO_PIN;
    gpio_initure.Mode = GPIO_MODE_AF_PP;
    gpio_initure.Pull = GPIO_PULLUP;
    gpio_initure.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(CAN_TX_GPIO_PORT, &gpio_initure); /* CAN_TX脚 模式设置 */

    gpio_initure.Pin = CAN_RX_GPIO_PIN;
    gpio_initure.Mode = GPIO_MODE_AF_INPUT;
    HAL_GPIO_Init(CAN_RX_GPIO_PORT, &gpio_initure); /* CAN_RX脚 必须设置成输入模式 */
  }
}

#if CAN_RX0_INT_ENABLE /* 使能RX0中断 */



/**
 * @brief       CAN RX0 中断服务函数
 *   @note      处理CAN FIFO0的接收中断
 * @param       无
 * @retval      无
 */
void USB_LP_CAN1_RX0_IRQHandler(void)
{
    uint8_t rxbuf[8];
    
    
    // 检查是否有接收数据
    if (HAL_CAN_GetRxFifoFillLevel(&g_canx_handler, CAN_RX_FIFO0) == 0)
    {
        return;
    }
    
    // 接收CAN消息
    if (HAL_CAN_GetRxMessage(&g_canx_handler, CAN_RX_FIFO0, &g_canx_rxheader, rxbuf) != HAL_OK)
    {
        return;
    }
    
    // 检查是否为标准数据帧
    if (g_canx_rxheader.IDE != CAN_ID_STD || g_canx_rxheader.RTR != CAN_RTR_DATA)
    {
        return;
    }
    
    // 处理实际速度消息
    if (g_canx_rxheader.StdId == CAN_MSG_ID_ACTUAL_SPEED)
    {
        if (g_canx_rxheader.DLC >= 5)  // 至少需要5字节数据
        {
            g_actual_speed = (int32_t)(rxbuf[0] | (rxbuf[1] << 8) | (rxbuf[2] << 16) | (rxbuf[3] << 24));
            uint8_t dir = rxbuf[4];
            if (dir == REVERSE_ROTATION)
            {
                g_actual_speed = -g_actual_speed;
            }
            g_speed_data_updated = 1;   // 设置数据更新标志
        }
    }
}

#endif

/**
 * @brief       CAN 发送一组数据
 *   @note      发送格式固定为: 标准ID, 数据帧
 * @param       id      : 标准ID(11位)
 * @retval      发送状态 0, 成功; 1, 失败;
 */
uint8_t can_send_msg(uint32_t id, uint8_t *msg, uint8_t len)
{
    uint32_t TxMailbox = CAN_TX_MAILBOX0;
    uint32_t timeout_start;
    uint32_t timeout_ms = 100;  // 100ms超时
    
    g_canx_txheader.StdId = id;         /* 标准标识符 */
    g_canx_txheader.ExtId = id;         /* 扩展标识符(29位) 标准标识符情况下，该成员无效*/
    g_canx_txheader.IDE = CAN_ID_STD;   /* 使用标准标识符 */
    g_canx_txheader.RTR = CAN_RTR_DATA; /* 数据帧 */
    g_canx_txheader.DLC = len;

    if (HAL_CAN_AddTxMessage(&g_canx_handler, &g_canx_txheader, msg, &TxMailbox) != HAL_OK) /* 发送消息 */
    {
        return 1;
    }
    
    // 使用超时机制等待发送完成，避免死机
    timeout_start = HAL_GetTick();
    while (HAL_CAN_GetTxMailboxesFreeLevel(&g_canx_handler) != 3)
    {
        if ((HAL_GetTick() - timeout_start) > timeout_ms)
        {
            // 超时处理：取消发送
            HAL_CAN_AbortTxRequest(&g_canx_handler, TxMailbox);
            printf("CAN Send Timeout\r\n");
            return 2; // 返回超时错误码
        }
    }
    
    return 0;
}

/**
 * @brief       CAN 接收数据查询
 *   @note      接收数据格式固定为: 标准ID, 数据帧
 * @param       id      : 要查询的 标准ID(11位)
 * @param       buf     : 数据缓存区
 * @retval      接收结果
 *   @arg       0   , 无数据被接收到;
 *   @arg       其他, 接收的数据长度
 */
uint8_t can_receive_msg(uint32_t id, uint8_t *buf)
{
  if (HAL_CAN_GetRxFifoFillLevel(&g_canx_handler, CAN_RX_FIFO0) == 0)     /* 没有接收到数据 */
  {
    return 0;
  }

  if (HAL_CAN_GetRxMessage(&g_canx_handler, CAN_RX_FIFO0, &g_canx_rxheader, buf) != HAL_OK)  /* 读取数据 */
  {
    return 0;
  }
  
  if (g_canx_rxheader.StdId!= id || g_canx_rxheader.IDE != CAN_ID_STD || g_canx_rxheader.RTR != CAN_RTR_DATA)       /* 接收到的ID不对 / 不是标准帧 / 不是数据帧 */
  {
    return 0;    
  }

  return g_canx_rxheader.DLC;
}

/**
 * @brief  通过CAN发送目标速度到G4控制板
 * @param  speed: 目标速度值 (可为负值，负值表示反转)
 * @return 0: 成功, 1: 发送失败, 2: 发送超时
 */
uint8_t send_target_speed_via_can(int32_t speed)
{
    uint8_t can_data[8] = {0};
    uint32_t abs_speed;
    uint8_t direction;
    
    // 处理速度值和方向
    if (speed >= 0)
    {
        abs_speed = (uint32_t)speed;
        direction = 0;  // 正转
    }
    else
    {
        abs_speed = (uint32_t)(-speed);
        direction = 1;  // 反转
    }
    
    // 按小端序打包速度值 (字节0-3)
    can_data[0] = (uint8_t)(abs_speed & 0xFF);
    can_data[1] = (uint8_t)((abs_speed >> 8) & 0xFF);
    can_data[2] = (uint8_t)((abs_speed >> 16) & 0xFF);
    can_data[3] = (uint8_t)((abs_speed >> 24) & 0xFF);
    
    // 方向信息 (字节4)
    can_data[4] = direction;
    
    // 保留字节 (字节5-7) 已初始化为0
    
    // 发送CAN消息
    uint8_t result = can_send_msg(0x100, can_data, 8);
    
    if (result == 0)
    {
        printf("CAN Send Success: Speed=%d, Dir=%s\r\n", 
               (int)abs_speed, direction ? "Reverse" : "Forward");
    }
    // else if (result == 1)
    // {
    //     printf("CAN Send Failed: AddTxMessage Error\r\n");
    // }
    // else if (result == 2)
    // {
    //     printf("CAN Send Failed: Timeout (100ms)\r\n");
    // }
    
    return result;
}

/**
 * @brief  获取实际速度值
 * @return 当前实际速度值
 */
int32_t get_actual_speed(void)
{
    return g_actual_speed;
}

/**
 * @brief  检查速度数据是否更新
 * @return 1: 有新数据, 0: 无新数据
 * @note   调用后会清除更新标志
 */
uint8_t is_speed_data_updated(void)
{
    if (g_speed_data_updated)
    {
        g_speed_data_updated = 0;  // 清除标志
        return 1;
    }
    return 0;
}

/**
 * @brief  通用CAN指令发送函数
 * @param  msg_id: CAN消息ID
 * @param  speed: 速度值 (可为负值，负值表示反转)
 * @return 0: 成功, 1: 发送失败, 2: 发送超时
 */
uint8_t send_can_command(uint32_t msg_id, int32_t speed)
{
    uint8_t can_data[8] = {0};
    uint32_t abs_speed;
    uint8_t direction;
    
    // 处理速度值和方向
    if (speed >= 0)
    {
        abs_speed = (uint32_t)speed;
        direction = 0;  // 正转
    }
    else
    {
        abs_speed = (uint32_t)(-speed);
        direction = 1;  // 反转
    }
    
    // 按小端序打包速度值 (字节0-3)
    can_data[0] = (uint8_t)(abs_speed & 0xFF);
    can_data[1] = (uint8_t)((abs_speed >> 8) & 0xFF);
    can_data[2] = (uint8_t)((abs_speed >> 16) & 0xFF);
    can_data[3] = (uint8_t)((abs_speed >> 24) & 0xFF);
    
    // 方向信息 (字节4)
    can_data[4] = direction;
    
    // 保留字节 (字节5-7) 已初始化为0
    
    // 发送CAN消息
    uint8_t result = can_send_msg(msg_id, can_data, 8);
    
    if (result == 0)
    {
        const char* cmd_name = "Unknown";
        switch(msg_id)
        {
            case 0x100: cmd_name = "TARGET_SPEED"; break;
            case 0x101: cmd_name = "STOP"; break;
            case 0x102: cmd_name = "START"; break;
        }
        printf("CAN Send Success [%s]: Speed=%d, Dir=%s\r\n", 
               cmd_name, (int)abs_speed, direction ? "Reverse" : "Forward");
    }
    
    return result;
}

